package lpg.tool.optionpg;

import java.util.EnumSet;
import java.util.Iterator;
import java.util.Map;
import lpg.OptionEnum;

class Node
{
    Node(String path, String name)
    {
        this.path = path;
        this.name = name;
    }

    boolean isLeaf()
    {
        return children == null;
    }

    void countOptions()
    {
        for (OptionEnum option : optionSet)
        {
            if (option.isSwitchOption())
            {
                switchOptionCount++;
                switchOption = option;
            }
            if (option.isValuedOption())
            {
                valuedOptionCount++;
                valuedOption = option;
            }
        }
        if (switchOptionCount > 1)
        {
            switchOption = null;
        }
        if (valuedOptionCount > 1)
        {
            valuedOption = null;
        }
    }

    void mergeChild(Node node)
    {
        if (node.path != null)
        {
            path += node.path;
        }
        else
        {
            pathSet = new PathSet();

            int pathLen = path.length();
            PathElement pathElement = null;
            PathSet pathElementBranch = null;
            for (PathElement element : node.pathSet.elemMap.values())
            {
                if (pathElement == null)
                {
                    // Concatenate the new path element
                    pathElement = new PathElement(path + element.path);
                    for (Map.Entry<Integer, PathSet> setEntry : element.branchSet.entrySet())
                    {
                        pathElement.branchSet.put(pathLen + setEntry.getKey(), setEntry.getValue());
                    }
                    pathSet.elemMap.put(path.charAt(0), pathElement);
                }
                else
                {
                    // Move other elements to the branch set of the new path element
                    if (pathElementBranch == null)
                    {
                        pathElementBranch = new PathSet();
                        pathElement.branchSet.put(pathLen, pathElementBranch);
                    }
                    pathElementBranch.elemMap.put(element.path.charAt(0), element);
                }
            }
            path = null;
        }
        children = node.children;
    }

    void mergeBrother(Node node)
    {
        toPathSet();
        node.toPathSet();
        pathSet.merge(node.pathSet);
    }

    private void toPathSet()
    {
        if (path != null)
        {
            PathElement pathElement = new PathElement(path);

            pathSet = new PathSet();
            pathSet.elemMap.put(path.charAt(0), pathElement);

            path = null;
        }
    }

    public String toString()
    {
        return toString("");
    }

    private String toString(String indent)
    {
        StringBuilder buf = new StringBuilder();
        buf.append(name);
        buf.append(optionSet);
        buf.append("[");
        buf.append(switchOption);
        buf.append("(");
        buf.append(switchOptionCount);
        buf.append(":");
        buf.append(valuedOptionCount);
        buf.append(")");
        buf.append(valuedOption);
        buf.append("]");
        if (path != null)
        {
            buf.append("\n");
            buf.append(indent);
            buf.append(children != null ? "|    " : "    ");
            buf.append(path);
        }
        else
        {
            buf.append(pathSet.toString(indent + "    ", 0));
        }
        if (children != null)
        {
            for (Iterator<Node> it = children.values().iterator(); it.hasNext();)
            {
                Node node = it.next();
                buf.append("\n");
                buf.append(indent);
                buf.append("|---");
                buf.append(node.toString(indent + (it.hasNext() ? "|   " : "    ")));
            }
        }
        return new String(buf);
    }

    String path = null;
    PathSet pathSet = null;
    final String name;

    EnumSet<OptionEnum> optionSet = EnumSet.noneOf(OptionEnum.class);

    Map<Character, Node> children = null;

    int switchOptionCount = 0;
    int valuedOptionCount = 0;
    // Unambiguous option if exists
    OptionEnum switchOption = null;
    OptionEnum valuedOption = null;

}
